package il.technion.cs236369.proxy.communication.http;

import il.technion.cs236369.proxy.ProxyHttpRequestHandler;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

import javax.net.ServerSocketFactory;

import org.apache.http.HttpException;
import org.apache.http.HttpResponseInterceptor;
import org.apache.http.impl.DefaultConnectionReuseStrategy;
import org.apache.http.impl.DefaultHttpResponseFactory;
import org.apache.http.impl.DefaultHttpServerConnection;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.CoreConnectionPNames;
import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.HttpProcessor;
import org.apache.http.protocol.HttpRequestHandler;
import org.apache.http.protocol.HttpRequestHandlerRegistry;
import org.apache.http.protocol.HttpService;
import org.apache.http.protocol.ImmutableHttpProcessor;

/**
 * a class implementing a basic behavior of an http proxy server. its in charge of listening on the socket and parsing a request,
 * the logic of dealing with the request is the responsibility of a HttpRequestHandler given in the initialization.
 * @author  itamar talmi 0396055979 itamar.talmi@gmail.com , ofir haviv 300228921 havivi1986@gmail.com
 *
 */
public class HttpProxyServer {
	private HttpParams params;
	private HttpService httpService;
	
	private ServerSocket serverSocket;
	private ProxyHttpRequestHandler requestHandler;

	/**
	 * create new setupHttpService.
	 * @param srvSockFactory - factory for server sockets.
	 * @param bindingPort - the relevant port to listen on.
	 * @param requestHandler - a handler that will handle the requests.
	 * @throws IOException
	 */
	public HttpProxyServer(ServerSocketFactory srvSockFactory, int bindingPort, ProxyHttpRequestHandler requestHandler) throws IOException {
		serverSocket = srvSockFactory.createServerSocket(bindingPort);
		this.requestHandler = requestHandler;
		setupHttpService(requestHandler);
	}


	private void setupHttpService(HttpRequestHandler requestHandler) {
		
		this.params = new BasicHttpParams();
		this.params
				.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, 5000)
				.setIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE,
						8 * 1024)
				.setBooleanParameter(
						CoreConnectionPNames.STALE_CONNECTION_CHECK, false)
				.setBooleanParameter(CoreConnectionPNames.TCP_NODELAY, true)
				.setParameter(CoreProtocolPNames.ORIGIN_SERVER,
						"HttpComponents/1.1");

		
		HttpProcessor httpproc = new ImmutableHttpProcessor( new HttpResponseInterceptor[] {});

		// Set up request handlers
		HttpRequestHandlerRegistry reqistry = new HttpRequestHandlerRegistry();
		reqistry.register("*", requestHandler);

		// Set up the HTTP service
		this.httpService = new HttpService(httpproc,
				new DefaultConnectionReuseStrategy(),
				new DefaultHttpResponseFactory(), reqistry, this.params);
	}

	/**
	 * accepts a new http request and pass request to the request handler given in the initialization.
	 * @throws IOException
	 * @throws HttpException
	 */
	public void getNewHttpRequest() throws IOException, HttpException{
		
		//System.out.println("Listening on port "	+ serverSocket.getLocalPort());
		//System.out.print("#");
		Socket connectionSocket = serverSocket.accept();		
		
	//	System.out.println("Incoming connection from " + connectionSocket.getInetAddress());

		try (DefaultHttpServerConnection httpConn = new DefaultHttpServerConnection()) {
			httpConn.bind(connectionSocket, this.params);
			HttpContext context = new BasicHttpContext(null);
			
			this.httpService.handleRequest(httpConn, context);
		} 
		catch (Exception e) {
			System.err.println("Error while tring to handle new client request");
			//System.err.println("Error in HttpProxyServer.getNewHttpRequest");
			//e.printStackTrace();
		}
		requestHandler.closeConnections();
	}


}
